/*
 *  Librarie de tipuri de date abstracte (TDA)
 
    Copyright (C) 2007  Catedra de Structuri de Date

    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation; either version 2 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License along
    with this program; if not, write to the Free Software Foundation, Inc.,
    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
 */
 
#include <stdlib.h>
#include <assert.h>
#include "arraylist.h"

#define GROW_RATE		0.50f

/*
 * Declaratii de structuri
 */


struct arraylist_adt {
	int count;
	int cap;
	
	void **data;
};

/*
 * Functii interne
 */
 
void _AL_Grow(ArrayList list, int desired) {
	assert(list);
	
	if (list->data != NULL) {
		if (desired > list->cap) {
			list->data = (void**)realloc(list->data, (int)((1+GROW_RATE)*desired)*sizeof(void*));
			list->cap = (int)((1 + GROW_RATE)*desired);
		}
	} else {
		list->data = (void**)malloc(desired*sizeof(void*));
		list->cap = desired;
		assert(list->count == 0);
	}
}

int _AL_ContainsIt(ArrayList list, ArrayListIterator it) {
	assert(list);
	
	if (it < 0 || it > list->count)
		return 0;
		
	return 1; 
}
 
 
/*
 * Functii publice
 */

ArrayList AL_New(int initcap) {
	ArrayList result;
	
	result = (ArrayList)malloc(sizeof(struct arraylist_adt));
	
	result->cap = 0;
	result->count = 0;
	result->data = NULL;
	
	if (initcap > 0)
		_AL_Grow(result, initcap);
	
	return result;
}


void AL_Free(ArrayList list) {
	if (list == NULL)
		return;
		
	if (list->data != NULL) {
		free(list->data);
	}
	
	free(list);
}

int AL_Empty(ArrayList list) {
	if (list == NULL)
		return -1;
		
	return (list->count == 0) ? 1 : 0;
}

int AL_Size(ArrayList list) {
	if (list == NULL)
		return -1;
		
	return list->count;
}


void *AL_Get(ArrayList list, ArrayListIterator it) {
	if (list == NULL)
		return NULL;
		
	if (_AL_ContainsIt(list, it)) {
		return list->data[it];
	}
	
	return NULL;
}

void AL_Update(ArrayList list, ArrayListIterator it, void *el) {
/*
TODO: N-ar fi mai bine sa elibereze memoria in momentul in care face update? La
apeluri repetate de update, se produc memory leaks.
*/
	if (list == NULL)
		return;
		
	if (_AL_ContainsIt(list, it)) {
		list->data[it] = el;
	}
}

ArrayListIterator AL_Find(ArrayList list, void *el) {
	int i;
	
	if (list == NULL)
		return -1;
		
	for (i=0; i < list->count; i++) {
		if (list->data[i] == el)
			return i;
	}
	
	return list->count;
}

void *AL_Remove(ArrayList list, ArrayListIterator it) {
	void *result = NULL;
	int i;
	
	if (list == NULL)
		return NULL;
		
	if (_AL_ContainsIt(list, it)) {
		if (it < list->count) {
			result = list->data[it];
			
			for (i=it; i < list->count - 1; i++) {
				list->data[i] = list->data[i+1];
			}
			
			list->count--;
			return result;		
		}
		else
			return NULL;
	}
	else
		return NULL;
}

void AL_Add(ArrayList list, ArrayListIterator it, void *el) {
	int i;
	
	if (list == NULL)
		return;
		
	if (_AL_ContainsIt(list, it)) {
		_AL_Grow(list, list->count+1);
		
		for (i = list->count; i > it; i--) {
			list->data[i] = list->data[i-1];
		}
		
		list->data[it] = el;
		
		list->count++;
	}
}

ArrayListIterator AL_Begin(ArrayList list) {
	if (list == NULL)
		return -1;
		
	return 0;
}

ArrayListIterator AL_End(ArrayList list) {
	if (list == NULL)
		return -1;
		
	return list->count;
}

ArrayListIterator AL_Next(ArrayList list, ArrayListIterator it) {
	if (list == NULL)
		return -1;
		
	if (_AL_ContainsIt(list, it)) {
		return (it + 1 > list->count) ? list->count : (it + 1);
	} else
		return -1;
}
